﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;


namespace Comp476Project
{
    class Minion:NPC
    {
        Map map;
        public List<Node> path;
        bool forward;
        public int current;
        public Color color;

        public ParticleSystem orb;
        Primitives3D.SpherePrimitive sphere;
        public ParticleSystem effects;
        bool startExpiryTime;
        public int timer=0;
        public Vector3 posOffset;
        public Vector3 tarOffset;
        Vector3 spPosv;
        public Minion(Primitives3D.SpherePrimitive sphere,Matrix proj, Model mod, Vector3 pos, Map m, int p, Color c)
            : base(sphere,proj, mod, pos)
        {
            spPosv = new Vector3(0, -100, 0);
            boosted = false;
            startExpiryTime = false;
            this.sphere = sphere;
            target = null;
            maxHealth = 500;
            health = maxHealth;
            damage = 0;
            baseDamage = 10;
            color = c;
            size = 0.005f;
            maxSpeed = 0.0008f;

            attackRange = 0.06f;

            viewRange = 0.1f;
            damageTimer = 0;
            attackTimer = 1000;
            
            map = m;

           
            choosePath(p);
            
            //character spawns at positive x side of the map
            if (color == Color.Blue)
            {
                forward = true;
                current = 1;
            }
            //if character spawns at opposite side of the map
            if (color == Color.Red)
            {
                forward = false;
                current = path.Count - 2;
            }

            

            behaviour = AIState.PATH;        

           //========particle system ========================
            Color smokeColor = Color.OrangeRed;
            float smokeDuration = 1500;
            //effects.Add(new ParticleSystem(particleModel, ref projection, type, N, duration, gravity, particleSize, sizeOffset, sizeMod, color, alphaChange, speed,
            //                                        speedOffset, rotate, sourcePosition, sourceOffset, direction, directionOffset, wind, spawn));
            effects = (new ParticleSystem(sphere, ref proj, 2, 1, smokeDuration, false, 0.006f, 0.0001f, 0.00001f, smokeColor, true, 0.004f,
                                                   0, false, new Vector3(0,-100,0), new Vector3(0f, 0f, 0f), new Vector3(0, 0, 0), new Vector3(0,0,0), new Vector3(0, 0, 0), 0));
            Color smokeColor1 = Color.Aqua;

            float smokeDuration1 = 200;
            orb = (new ParticleSystem(sphere, ref proj, 2, 1, smokeDuration1, false, 0.009f, 0.00005f, 0.00009f, smokeColor1, false, 0.0001f,
                                                 0.00009f, false, new Vector3(0, -100, 0), new Vector3(0.00025f, 0.00010f, 0.00025f), new Vector3(0, 0.00000001f, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), 0));
        }

        public override void update(GameTime gameTime)
        {
            if (boosted)
            {
                baseDamage = 20;
                orb.position.X = position.X;
                orb.position.Y = position.Y + 0.020f;
                orb.position.Z = position.Z;
                
                orb.Update(gameTime);
            }
            else
            {
                baseDamage = 10;
            }
            if(behaviour==AIState.PATH)
            followPath(path);
            if (behaviour == AIState.ATTACK)
            {
                
                posOffset.X = position.X;
                posOffset.Y = position.Y; //+0.00001f;
                posOffset.Z = position.Z;
                effects.position = posOffset;
                tarOffset.X = target.position.X;
                tarOffset.Y = target.position.Y; //+0.00001f;
                tarOffset.Z = target.position.Z;

                effects.direction = direction;//((tarOffset) - posOffset);
               
                effects.direction.Normalize();
                effects.position += effects.direction * effects.speed;
                //timer += (int)gameTime.ElapsedGameTime.TotalMilliseconds;
               // effects.Update(gameTime);
              
                attack(gameTime);
            }
            if (target != null)
            {
                behaviour = AIState.ATTACK;

            }
            else
            {
                startExpiryTime = false;
            }
            //============== particles update============
            //if (timer > 0 && timer <= 30)
            //{
            //    startExpiryTime = true;
            //    effects.Update(gameTime);
            //}
            //else if (timer > 30)
            //{
            //    startExpiryTime = false;
            //    timer = 0;
            //}
            //=======particles update=============
            mapYPosition(map.grid,map.mapSize);
            base.update(gameTime);
        }

        public override void draw(Matrix view)
        {

            //if (startExpiryTime == true)
            if(behaviour ==  AIState.ATTACK)
            {
              //effects.Draw(view);
               
                //spPosv += direction * 0.01f;
                sphere.Draw(Matrix.CreateScale(0.003f)*Matrix.CreateTranslation(spPosv), view, projection, Color.Red);
            }
            if (boosted == true)
            {
                orb.Draw(view);
            }
            base.draw(view);
        }
        public void steeringArrive(Vector3 target)
        {
            

            Vector3 aim = target - this.position;
            float distance = aim.Length();
            aim.Normalize();

            float ang = (float)Math.Acos(Vector3.Dot(aim, this.direction));

            Vector3 cross = Vector3.Cross(aim, this.direction);
            if (cross.Y < 0)
                ang *= -1;

            if (ang < -0.1)
                direction = Vector3.Transform(direction, Matrix.CreateRotationY(0.1f));
            else if (ang > 0.1)
                direction = Vector3.Transform(direction, Matrix.CreateRotationY(-0.1f));


                speed = maxSpeed;
                this.direction.Normalize();

                this.velocity = this.direction * this.speed;
                this.position += this.velocity;

            //}




        }

        public void followPath(List<Node> list) 
        {
            
            
            steeringArrive(list[current].position);
            //steeringArrive(new Vector3(10,0,10));
                
            if ((position - list[current].position).Length() <= 0.05f)
            {
                
                if (forward)
                    current++;
                else
                    current--;
                    
            }
            if (current == path.Count - 1)
                forward = false;
            if (current == 0)
                forward = true;
            
            
            
        }

        public void mapYPosition(float[][] map, int size)
        {
            int xVal = (int)Math.Round(((position.X - (-1)) * size) / 2);
            int zVal = (int)Math.Round(((position.Z - (-1)) * size) / 2);



            if (map[xVal][zVal] != 0)
            {
                float diff = Math.Abs(position.Y - map[xVal][zVal]);

                if (diff > 0.01)
                {
                    position.Y = ((map[xVal][zVal] - position.Y) / 2) + position.Y;

                }
                else
                    position.Y = map[xVal][zVal];
            }
        }//end of function

        public void choosePath(int p)
        {
            if (p == 1)
                path = map.leftPath;
            else if (p == 2)
                path = map.midPath;
            else if (p == 3)
                path = map.rightPath;

        }

        public virtual void attack(GameTime gameTime) 
        {
            Vector3 aim = target.position - this.position;
            float distance = aim.Length();
            aim.Normalize();

            float ang = (float)Math.Acos(Vector3.Dot(aim, this.direction));

            Vector3 cross = Vector3.Cross(aim, this.direction);
            if (cross.Y < 0)
                ang *= -1;

            if (ang < -0.1)
                direction = Vector3.Transform(direction, Matrix.CreateRotationY(0.1f));
            else if (ang > 0.1)
                direction = Vector3.Transform(direction, Matrix.CreateRotationY(-0.1f));


            float distToTarget = Vector3.Distance(position, path[current].position);
            if (distToTarget > viewRange) 
            {
                target = null;

                behaviour = AIState.PATH;
            }

            if (distance <= attackRange)
            {

                speed = 0;
                damageTimer+= (int)gameTime.ElapsedGameTime.TotalMilliseconds;
                //attack animation
                if(damageTimer>=attackTimer && target!=null)
                {
                    damage = baseDamage +(float)rand.NextDouble() * (3*baseDamage);
                    spPosv = new Vector3(position.X, position.Y + 0.008f, position.Z);
                    target.health -= damage;
                    //Console.WriteLine("attack");
                    damageTimer = 0;
                }
                spPosv += direction * 0.001f;         
                if ( target!=null && target.health <= 0 ) 
                {
                    
                    target = null;
                   
                    behaviour = AIState.PATH;
                }

            }

            else
            {
            speed = maxSpeed;
            this.direction.Normalize();

            this.velocity = this.direction * this.speed;
            this.position += this.velocity;

            }





        }// end of attack

        private void nextNode() 
        {
            float min=2;
            
            for(int i=0;i<path.Count;++i)
            {
                float dist = (path[i].position - position).Length();
                if (dist <= min)
                    current = i;

            }
        }

    }
}
